/*
 *
 *  Copyright 2015 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *
 */

package com.net.xpay.operator.swagger;

import com.fasterxml.classmate.ResolvedType;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.net.common.enums.base.IntEnum;
import com.net.common.enums.base.StringEnum;
import com.net.common.util.SerializationUtil;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static springfox.documentation.schema.Annotations.findPropertyAnnotation;
import static springfox.documentation.swagger.schema.ApiModelProperties.findApiModePropertyAnnotation;

@Component
@Profile({"dev", "local"})
public class CustomApiModelPropertyPropertyBuilder implements ModelPropertyBuilderPlugin {
    private final DescriptionResolver descriptions;

    @Autowired
    public CustomApiModelPropertyPropertyBuilder(DescriptionResolver descriptions) {
        this.descriptions = descriptions;
    }

    @Override
    public void apply(ModelPropertyContext context) {
        Optional<ApiModelProperty> annotation = Optional.absent();

        if (context.getAnnotatedElement().isPresent()) {
            annotation = annotation.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
        }
        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = annotation.or(findPropertyAnnotation(
                    context.getBeanPropertyDefinition().get(),
                    ApiModelProperty.class));
        }

        final Class<?> rawPrimaryType = context.getBeanPropertyDefinition().get().getRawPrimaryType();
        //IntEnum
        if (annotation.isPresent() && IntEnum.class.isAssignableFrom(rawPrimaryType)) {
            IntEnum[] values = (IntEnum[]) rawPrimaryType.getEnumConstants();
            if (values == null || values.length == 0) {
                return;
            }
            //设置可选值
            final List<String> displayValues = Arrays.stream(values).map(intEnum -> Integer.toString(intEnum.getValue())).collect(Collectors.toList());
            final AllowableListValues allowableListValues = new AllowableListValues(displayValues, rawPrimaryType.getTypeName());

            //说明
            String desc = buildDescriptionOfIntEnum(annotation.get(), values);

            //固定设置为int类型
            final ResolvedType resolvedType = context.getResolver().resolve(int.class);
            context.getBuilder()
                    .allowableValues(allowableListValues)
                    .type(resolvedType)
                    .description(desc)
                    .example(values[0].getValue());
            return;
        }
        //StringEnum
        if (annotation.isPresent() && StringEnum.class.isAssignableFrom(rawPrimaryType)) {
            StringEnum[] values = (StringEnum[]) rawPrimaryType.getEnumConstants();
            if (values == null || values.length == 0) {
                return;
            }

            //设置可选值
            final List<String> displayValues = Arrays.stream(values).map(StringEnum::getValue).collect(Collectors.toList());
            final AllowableListValues allowableListValues = new AllowableListValues(displayValues, rawPrimaryType.getTypeName());

            //说明
            String desc = buildDescriptionOfStringEnum(annotation.get(), values);

            //固定设置为int类型
            final ResolvedType resolvedType = context.getResolver().resolve(String.class);
            context.getBuilder()
                    .allowableValues(allowableListValues)
                    .type(resolvedType)
                    .description(desc)
                    .example(values[0].getValue());
            return;
        }

        //LocalDateTime
        if (annotation.isPresent() && LocalDateTime.class.isAssignableFrom(rawPrimaryType)) {
            context.getBuilder().example(SerializationUtil.obj2String(LocalDateTime.now()));
            return;
        }

        //LocalDate
        if (annotation.isPresent() && LocalDate.class.isAssignableFrom(rawPrimaryType)) {
            context.getBuilder().example(SerializationUtil.obj2String(LocalDate.now()));
            return;
        }
    }

    private String buildDescriptionOfIntEnum(ApiModelProperty annotation, IntEnum[] values) {
        String description = getDescriptionFromApiModelProperty(annotation);

        if (values == null || values.length == 0) {
            return description;
        }

        StringBuilder descBuilder = new StringBuilder();
        int idx = 0;
        for (IntEnum intEnum : values) {
            descBuilder.append(intEnum.getValue()).append(" ").append(intEnum.getDesc());
            idx++;
            if (idx != values.length) {
                descBuilder.append(", ");
            }
        }
        return description + ":" + descBuilder;
    }

    private String buildDescriptionOfStringEnum(ApiModelProperty annotation, StringEnum[] values) {
        String description = getDescriptionFromApiModelProperty(annotation);

        if (values == null || values.length == 0) {
            return description;
        }

        StringBuilder descBuilder = new StringBuilder();
        int idx = 0;
        for (StringEnum stringEnum : values) {
            descBuilder.append(stringEnum.getValue()).append(" ").append(stringEnum.getDesc());
            idx++;
            if (idx != values.length) {
                descBuilder.append(", ");
            }
        }
        return description + ":" + descBuilder;
    }

    private String getDescriptionFromApiModelProperty(ApiModelProperty annotation) {
        String description = "";
        if (!Strings.isNullOrEmpty(annotation.value())) {
            description = annotation.value();
        } else if (!Strings.isNullOrEmpty(annotation.notes())) {
            description = annotation.notes();
        }

        return descriptions.resolve(description);
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}
